iT邦幫忙

2022 iThome 鐵人賽

DAY 28
2

情境

有時候我們會需要隱藏網頁上的元件。

舉幾個例子來說,例如我們為了使用者體驗的目的,我們希望在某某情境下,某些按鈕不要被使用者誤觸,或是為了畫面上排版的簡潔,我們希望並不是所有有關無關的元件都一股腦兒全部出現在畫面上。

為了使用者體驗考量而隱藏或顯示

以下圖為例,這是一個 MUI 的 Table 元件,當有任何一行資料被勾選時,Table 的頂部才會出現「垃圾桶」圖案的刪除按鈕,避免使用者在沒有勾選任何資料時按下刪除按鈕:

為了權限考量而隱藏或顯示

或者,在某些網站上,不同使用者有不同的權限,某些按鈕我們會希望擁有特定權限的人才能夠看到。例如下圖的 Admin Management 選單,我們希望只有管理者才能夠透過他進入後台頁面:

但無論如何,我們總會有許多不同的需求和理由想要隱藏或顯示某些網頁元件,我們今天要來討論實作的方法以及他會帶來的不同效果。

你能看見多遠的未來呢?

我們常用來隱藏網頁元件的 CSS 有 display, visiblity, opacity。下面是他們分別用來隱藏時的語法:

.element-a {
  display: none;
}

.element-b {
  visibility: hidden;
}

.element-c {
  opacity: 0;
}

雖然好像都是隱藏元件,但是他們在各方面可能有一些細微的差異,如果用錯地方了,有可能會造成一些錯誤,甚至釀成災難。

元件是否佔據頁面空間

當我們設置 display: none; 的時候,我們會發現該元件仍然會在 DOM 裡面,但是不會佔據頁面空間:

<div class="container">
  <div class="group">
    <div class="box">Top</div>
    <div class="box">Bottom</div>
  </div>
  <div class="group">
    <div class="box" style="display: none;">Top</div>
    <div class="box">Bottom</div>
  </div>
</div>

從上圖我們可以看到,我們分別有兩個 group,其中,右邊得這個 Top-Bottom group,我們將 Top 透過 display 設置為隱藏之後,雖然 DOM 元件還在,但是在畫面上,Top Box 不但不見了,而且畫面上不會為他保留空間,所以 Bottom Box 就順勢往上推了。

而如果我們使用 visibility: hidden; 或是 opacity: 0; 來隱藏元件,一樣 DOM 元件還在,但是我們可以看到 Top Box 原本佔據的空間被保留,因此 Bottom Box 不會往上推去佔據 Top Box 的空間:

<div class="container">
  <div class="group">
    <div class="box">Top</div>
    <div class="box">Bottom</div>
  </div>
  <div class="group">
    <div class="box" style="visibility: hidden;">Top</div>
    <div class="box">Bottom</div>
  </div>
  <div class="group">
    <div class="box" style="opacity: 0;">Top</div>
    <div class="box">Bottom</div>
  </div>
  <div class="group">
    <div class="box" style="display: none;">Top</div>
    <div class="box">Bottom</div>
  </div>
</div>

對子元件的影響

如果子元件沒有對應的設置的話,都會繼承父元件的樣式,也就是說會受到父元件設置的屬性所影響,換句話說,就是會跟父元件顯示的效果一樣。

因此我們要來看一下,如果子元件跟父元件設置「不同」的話,是否會有作用呢?

下面這是我們實驗的結構:

.container {
  display: flex;
  gap: 30px;
}

.center {
  display: flex;
  justify-content: center;
  align-items: center;    
}

.parent {
  width: 200px;
  height: 200px;
  background: #1F7A8C;
}

.children {
  width: 100px;
  height: 100px;
  background: #BFDBF7;
}
<div class="container">
  <div class="center parent">
    <div class="center children">Normal</div>
  </div>
  <div class="center parent" style="visibility: hidden;">
    <div class="center children" style="visibility: visible;">visibility</div>
  </div>
  <div class="center parent" style="opacity: 0;">
    <div class="center children" style="opacity: 1;">opacity</div>
  </div>
  <div class="center parent" style="display: none;">
    <div class="center  children" style="display: block;">display</div>
  </div>
</div>

從左到右依序是:

  • 父元件、子元件完全不設置,當作對照組。
  • 使用 visibility,父元件 visibility: hidden;,子元件 visibility: visible;
  • 使用 opacity,父元件 opacity: 0;,子元件 opacity: 1;
  • 使用 display,父元件 display: none;,子元件 display: block;

我們可以發現,父子元件設置不同時,只有 visibility 在子元件的設置有起到作用,也就是父元件隱藏,但子元件顯示。其他兩個的子元件的設置,在父元件設置隱藏的前提之下,都沒有起到作用。

自身綁定的事件是否仍然能夠觸發

在上述的範例當中,我們在每一個被隱藏的元件上面綁定點擊事件,點擊之後跳出 alert,來看看是否仍然能夠順利被觸發。

<div class="container">
  <div class="center parent">
    <div class="center children">Normal</div>
  </div>
  <div class="center parent" style="visibility: hidden;" onclick="alert('visibility')">
    <div class="center children" style="visibility: visible;">visibility</div>
  </div>
  <div class="center parent" style="opacity: 0;" onclick="alert('opacity')">
    <div class="center children" style="opacity: 1;">opacity</div>
  </div>
  <div class="center parent" style="display: none;" onclick="alert('display')">
    <div class="center  children" style="display: block;">display</div>
  </div>
</div>

visibility

在 visibility 當中,因為父元件被設置為 visibility: hidden;,因此,點擊父元件時,alert() 「不會」被觸發。

但很神奇的事是,因為我們子元件設置為 visibility: visible;,所以點擊子元件時,會因為事件冒泡(從啟動事件的元件節點開始,逐層往上傳遞),而讓父元件的 alert() 事件被觸發。

opacity

在 opacity 當中,不管是點擊父元件還是子元件,alert() 事件都還是會被觸發,就好像是他一直在那邊,只是我們看不到而已。

display

設置 display 的元件,雖然元件在 DOM 當中仍然存在,但是因為在畫面上已經不佔據空間,所以我們連點擊他的機會都沒有,因此無法觸發 alert() 事件。

是否支持 transition

opacity

在 opacity 當中,因為他原意是設置元素的透明度,因此,透明度在 0 到 1 之間,能夠有過度的動畫也是很好理解的,整體的效果就是淡入淡出:

.block {
  width: 200px;
  height: 200px;
  background: #1F7A8C;
  opacity: 0;
  transition: opacity 0.2s ease-in-out;
}

.block:hover {
  opacity: 1;
}
<div class="container">
  <div class="block"></div>
</div>

visibility

如果同樣的設置,只是改為 visibility,我們會發現,初始狀態是 visibility: visible;,經過 hover 之後要變成 visibility: hidden;,他會經過一段延遲之後消失,並不會有淡入淡出的效果:

.block {
  width: 200px;
  height: 200px;
  background: #1F7A8C;
  visibility: visible;
  transition: visibility 0.2s ease-in-out;
}

.block:hover {
  visibility: hidden;
}

如果反過來,初始狀態是 visibility: hidden;,經過 hover 之後要變成 visibility: visible;,則是不起作用。

.block {
  width: 200px;
  height: 200px;
  background: #1F7A8C;
  visibility: hidden;
  transition: visibility 0.2s ease-in-out;
}

.block:hover {
  visibility: visible;
}

但是還有一個很神奇的地方,就是如果今天有 A, B 兩個 block,B 的初始狀態是 hidden,A 是 visible,如果要求 hover 在 A 上面,然後要讓 B 變成 visible,卻是可以的喔!

.block {
  width: 200px;
  height: 200px;
}

.block__a {
  background: #B5E2FA;
}

.block__b {
  background: #0FA3B1;
  visibility: hidden;
}

.block__a:hover + .block__b {
  visibility: visible;
}
<div class="container">
  <div class="block block__a">Block A</div>
  <div class="block block__b">Block B</div>
</div>

display

display 他不僅不支持 transition 的效果,連帶跟他扯上關係的 transition 也會跟著失效!如下範例,我們在 Block B 上面同時設置 opacity: 0; 以及 display: none;,連帶在 hover 的時候,就算把 display 設置為 block,其 opacity 的 transition 也會失效:

.block__a {
  width: 200px;
  height: 200px;
  background: #B5E2FA;
}

.block__b {
  width: 100px;
  height: 100px;
  background: #0FA3B1;
  opacity: 0;
  display: none;
  transition: 1s
}

.block__a:hover .block__b {
  opacity: 1;
  display: block;
}
<div class='block__a'>
  <div class='block__b'></div>
</div>

這是因為 display:none; 的元素,是不會渲染在頁面上的,而 transition 要起作用,元素必須是已經渲染在頁面上的元素。

小結

雖然都是元件的隱藏,但是因為使用不同的屬性,會對頁面造成不同的效果。

如果我們要對元件做淡入淡出的 transition,那我們會比較建議選擇 opacity。如果我們元件消失之後,不會保留業面上的空間,能夠讓後面的元件遞補上來,做整齊的排列,那我們可能可以選擇 display: none;

那如果我們是要做權限控制,那我們應該選擇哪一個呢?假使我們要讓某個元件消失在 DOM 上面,那我們使用者三個屬性都不適合,因為我們想要隱藏的東西雖然在畫面上看不到,但是透過開發者模式來檢視原始碼的時候,還是會透過 DOM 來找到該元件,因此可能因為權限關係被隱藏的按鈕就會因此暴露了。所以此時,我們可以使用 JavaScript 來讓元件根本上從 DOM 消失,來確保權限的安全。

因此,因著不同的情境我們會需要不同的屬性和作法,希望瞭解每個元件的細節之後,我們在面對不同情境時,能夠做出正確的選擇,防止意外的發生。


上一篇
【Day27】CSS 語法 - 分組選擇器
下一篇
【Day29】CSS 語法 - 失效的 z-index
系列文
防禦性 CSS - 建立「防患未然」的匠人心態30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言